package org.eiahe.hr.attendance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.ehe.common.core.utils.MapstructUtils;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.eiahe.hr.attendance.domain.AttendanceRecord;
import org.eiahe.hr.attendance.domain.Holiday;
import org.eiahe.hr.attendance.domain.LeaveApplication;
import org.eiahe.hr.attendance.domain.MonthlyAttendanceStat;
import org.eiahe.hr.attendance.domain.bo.MonthlyAttendanceStatBo;
import org.eiahe.hr.attendance.domain.vo.LeaveApplicationVo;
import org.eiahe.hr.attendance.domain.vo.MonthlyAttendanceStatVo;
import org.eiahe.hr.attendance.mapper.AttendanceRecordMapper;
import org.eiahe.hr.attendance.mapper.HolidayMapper;
import org.eiahe.hr.attendance.mapper.LeaveApplicationMapper;
import org.eiahe.hr.attendance.mapper.MonthlyAttendanceStatMapper;
import org.eiahe.hr.attendance.service.IMonthlyAttendanceStatService;
import org.ehe.system.domain.HrEmployee;
import org.ehe.system.mapper.HrEmployeeMapper;
import org.eiahe.hr.filler.EmployeeNameFiller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 月度考勤统计Service业务层处理
 *
 * @author zhangnn
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class MonthlyAttendanceStatServiceImpl implements IMonthlyAttendanceStatService {

    private final MonthlyAttendanceStatMapper baseMapper;
    private final AttendanceRecordMapper attendanceRecordMapper;
    private final LeaveApplicationMapper leaveApplicationMapper;
    private final HolidayMapper holidayMapper;
    private final HrEmployeeMapper employeeMapper;
    private final EmployeeNameFiller employeeNameFiller;

    @Override
    public TableDataInfo<MonthlyAttendanceStatVo> queryPageList(MonthlyAttendanceStatBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<MonthlyAttendanceStat> lqw = buildQueryWrapper(bo);
        Page<MonthlyAttendanceStatVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        // 填充员工姓名
        employeeNameFiller.fillEmployeeNames(result.getRecords(),
            MonthlyAttendanceStatVo::getEmployeeNo,
            MonthlyAttendanceStatVo::setEmployeeName);
        return TableDataInfo.build(result);
    }

    @Override
    public List<MonthlyAttendanceStatVo> queryList(MonthlyAttendanceStatBo bo) {
        LambdaQueryWrapper<MonthlyAttendanceStat> lqw = buildQueryWrapper(bo);
        List<MonthlyAttendanceStatVo> list = baseMapper.selectVoList(lqw);
        // 填充员工姓名
        employeeNameFiller.fillEmployeeNames(list,
            MonthlyAttendanceStatVo::getEmployeeNo,
            MonthlyAttendanceStatVo::setEmployeeName);

        return list;
    }

    private LambdaQueryWrapper<MonthlyAttendanceStat> buildQueryWrapper(MonthlyAttendanceStatBo bo) {
        LambdaQueryWrapper<MonthlyAttendanceStat> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getYear() != null, MonthlyAttendanceStat::getYear, bo.getYear());
        lqw.eq(bo.getMonth() != null, MonthlyAttendanceStat::getMonth, bo.getMonth());
        lqw.eq(StringUtils.isNotBlank(bo.getEmployeeNo()), MonthlyAttendanceStat::getEmployeeNo, bo.getEmployeeNo());
        lqw.eq(bo.getUserId() != null, MonthlyAttendanceStat::getUserId, bo.getUserId());
        lqw.orderByDesc(MonthlyAttendanceStat::getYear);
        lqw.orderByDesc(MonthlyAttendanceStat::getMonth);
        return lqw;
    }

    @Override
    public MonthlyAttendanceStatVo queryById(Long statId) {
        MonthlyAttendanceStatVo vo = baseMapper.selectVoById(statId);
        employeeNameFiller.fillEmployeeName(vo,
            MonthlyAttendanceStatVo::getEmployeeNo,
            MonthlyAttendanceStatVo::setEmployeeName);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateMonthlyStats(Integer year, Integer month) {
        log.info("开始生成{}年{}月的月度考勤统计", year, month);

        // 查询所有在职员工
        List<HrEmployee> employees = employeeMapper.selectList(
                Wrappers.lambdaQuery(HrEmployee.class)
                        .eq(HrEmployee::getEmployeeStatus, "1")); // 1-在职

        for (HrEmployee employee : employees) {
            try {
                regenerateEmployeeStats(employee.getEmployeeNo(), year, month);
            } catch (Exception e) {
                log.error("生成员工{}的月度统计失败", employee.getEmployeeNo(), e);
            }
        }

        log.info("月度考勤统计生成完成,共处理{}个员工", employees.size());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void regenerateEmployeeStats(String employeeNo, Integer year, Integer month) {
        log.info("开始生成员工{}的{}年{}月考勤统计", employeeNo, year, month);

        // 查询员工信息
        HrEmployee employee = employeeMapper.selectOne(
                Wrappers.lambdaQuery(HrEmployee.class)
                        .eq(HrEmployee::getEmployeeNo, employeeNo)
                        .last("LIMIT 1"));

        if (employee == null) {
            log.warn("员工不存在: {}", employeeNo);
            return;
        }

        // 计算月份的日期范围
        YearMonth yearMonth = YearMonth.of(year, month);
        LocalDate startDate = yearMonth.atDay(1);
        LocalDate endDate = yearMonth.atEndOfMonth();

        // 删除已存在的统计记录
//        baseMapper.delete(Wrappers.lambdaQuery(MonthlyAttendanceStat.class)
//                .eq(MonthlyAttendanceStat::getEmployeeNo, employeeNo)
//                .eq(MonthlyAttendanceStat::getYear, year)
//                .eq(MonthlyAttendanceStat::getMonth, month));

        // 查询考勤记录
        List<AttendanceRecord> records = attendanceRecordMapper.selectList(
                Wrappers.lambdaQuery(AttendanceRecord.class)
                        .eq(AttendanceRecord::getEmployeeNo, employeeNo)
                        .between(AttendanceRecord::getAttendanceDate, startDate, endDate));

        // 统计各项数据
        MonthlyAttendanceStat stat = new MonthlyAttendanceStat();
        stat.setEmployeeNo(employeeNo);
        stat.setUserId(employee.getUserId());
        stat.setYear(year);
        stat.setMonth(month);

        // 计算应出勤天数(工作日天数)
        int workDays = calculateWorkDays(startDate, endDate);
        stat.setWorkDays(workDays);

        // 统计考勤状态
        Map<String, Integer> statusCount = new HashMap<>();
        for (AttendanceRecord record : records) {
            String status = record.getAttendanceStatus();
            statusCount.put(status, statusCount.getOrDefault(status, 0) + 1);
        }

        stat.setNormalDays(statusCount.getOrDefault("0", 0)); // 正常
        stat.setLateCount(statusCount.getOrDefault("1", 0)); // 迟到
        stat.setEarlyCount(statusCount.getOrDefault("2", 0)); // 早退
        stat.setAbsenceCount(statusCount.getOrDefault("3", 0)); // 旷工

        // 计算实际出勤天数(正常+迟到+早退,排除旷工)
        int normalDays = (int) records.stream()
                .filter(r -> "0".equals(r.getAttendanceStatus()) ||
                        "1".equals(r.getAttendanceStatus()) ||
                        "2".equals(r.getAttendanceStatus()))
                .map(AttendanceRecord::getAttendanceDate)
                .distinct()
                .count();
        stat.setNormalDays(normalDays);

        // 统计请假天数
        Map<String, BigDecimal> leaveDaysMap = calculateLeaveDays(employeeNo, startDate, endDate);
        stat.setPersonalLeaveDays(leaveDaysMap.getOrDefault("1", BigDecimal.ZERO));
        stat.setSickLeaveDays(leaveDaysMap.getOrDefault("2", BigDecimal.ZERO));
        stat.setAnnualLeaveDays(leaveDaysMap.getOrDefault("3", BigDecimal.ZERO));
        stat.setCompensatoryLeaveDays(leaveDaysMap.getOrDefault("4", BigDecimal.ZERO));
        // 将各种假期天数合并到otherLeaveDays字段中
        BigDecimal otherLeaveDays = BigDecimal.ZERO
                .add(leaveDaysMap.getOrDefault("5", BigDecimal.ZERO)) // 婚假
                .add(leaveDaysMap.getOrDefault("6", BigDecimal.ZERO)) // 产假
                .add(leaveDaysMap.getOrDefault("7", BigDecimal.ZERO)) // 陪产假
                .add(leaveDaysMap.getOrDefault("8", BigDecimal.ZERO)); // 丧假
        stat.setOtherLeaveDays(otherLeaveDays);

        // 判断异常状态(迟到≥3次或旷工≥1次标记为异常)
        if (stat.getLateCount() >= 3 || stat.getAbsenceCount() >= 1) {
            stat.setStatus("1"); // 异常
            stat.setRemark(String.format("迟到%d次,旷工%d次", stat.getLateCount(), stat.getAbsenceCount()));
        } else {
            stat.setStatus("0"); // 正常
        }
        stat.setDelFlag("0");

        MonthlyAttendanceStat monthlyAttendanceStat = this.baseMapper.selectOne(new LambdaQueryWrapper<MonthlyAttendanceStat>()
            .eq(MonthlyAttendanceStat::getEmployeeNo, employeeNo)
            .eq(MonthlyAttendanceStat::getYear, year)
            .eq(MonthlyAttendanceStat::getMonth, month)
        );

        if(Objects.nonNull(monthlyAttendanceStat)){
            stat.setStatId(monthlyAttendanceStat.getStatId());
            baseMapper.updateById(stat);
        }else {
            // 保存统计记录
            baseMapper.insert(stat);
        }
        log.info("员工{}的{}年{}月考勤统计生成完成", employeeNo, year, month);
    }

    /**
     * 计算工作日天数
     */
    private int calculateWorkDays(LocalDate startDate, LocalDate endDate) {
        int workDays = 0;
        LocalDate current = startDate;

        while (!current.isAfter(endDate)) {
            // 查询是否是节假日
            Long count = holidayMapper.selectCount(
                    Wrappers.lambdaQuery(Holiday.class)
                            .eq(Holiday::getHolidayDate, current));

            // 不是周末且不是节假日
            if (current.getDayOfWeek().getValue() < 6 && (count == null || count == 0)) {
                workDays++;
            }

            current = current.plusDays(1);
        }

        return workDays;
    }

    /**
     * 计算请假天数
     */
    private Map<String, BigDecimal> calculateLeaveDays(String employeeNo, LocalDate startDate, LocalDate endDate) {
        Map<String, BigDecimal> leaveDaysMap = new HashMap<>();

        // 查询该时间段内已通过的请假申请
        List<LeaveApplication> leaveApplications = leaveApplicationMapper.selectList(
                Wrappers.lambdaQuery(LeaveApplication.class)
                        .eq(LeaveApplication::getEmployeeNo, employeeNo)
                        .eq(LeaveApplication::getApprovalStatus, "3") // 已通过
                        .le(LeaveApplication::getStartTime, endDate.atTime(23, 59, 59))
                        .ge(LeaveApplication::getEndTime, startDate.atStartOfDay()));

        // 按请假类型汇总天数
        for (LeaveApplication leave : leaveApplications) {
            String leaveType = leave.getLeaveType();
            BigDecimal days = leave.getLeaveDays() != null ? leave.getLeaveDays() : BigDecimal.ZERO;

            leaveDaysMap.merge(leaveType, days, BigDecimal::add);
        }

        return leaveDaysMap;
    }
}
